package com.github.hgkmail.hello.interview.multithread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

//测试 synchronized 和 ReentrantLock
//synchronized: 非公平 可重入 基于管程
//ReentrantLock: 非公平/公平 可重入 基于AQS
public class TestSynchronized {
    private Object syncObject=new Object();
    private static Object staticSyncObject=new Object();
    private ReentrantLock reentrantLock=new ReentrantLock(); //ReentrantLock默认是非公平锁，与synchronized一样（非公平 可重入）

    public static final int THREAD_NUM=10;
    volatile int sum=0; //volatile只能用作成员变量，不能用于局部变量！

    //实际打印sum的值不是10000，而是一个随机数
    public void raceCondition() throws InterruptedException {
        List<Thread> threads=new ArrayList<>();
        for (int i = 0; i < THREAD_NUM; i++) {
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        sum++; //这里sum++并不是原子操作，可以分解成好几条字节码指令，volatile只保证可见性，不保证原子性
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) { //sleep过程中调用interrupt()会抛出InterruptedException
                            e.printStackTrace();
                        }
                    }
                }
            });
            threads.add(thread);
            thread.start();
        }
        for (Thread t:threads) {
            t.join(); //join：阻塞等待线程执行完成
        }
    }

    //普通方法加 synchronized 和 synchronized(this) 本质一样，共享对象是this
    synchronized void increaseSum() {
        sum++;
    }

    void increaseSum2() {
        synchronized (this) { //同一时刻只能一个线程进入[同步块]，其他线程处于等待队列，等通知
            synchronized (this) { //演示可重入
                synchronized (this) {
                    sum += 2;
                }
            }
        }
    }

    void increaseSum3() {
        synchronized (syncObject) { //syncObject 不能为空
            synchronized (syncObject) { //演示可重入
                synchronized (syncObject) {
                    sum += 3;
                }
            }
        }
    }

    void increaseSum4() {
        synchronized (staticSyncObject) { //staticSyncObject 不能为空，可以跨实例加锁
            sum+=4;
        }
    }

    void increaseSum5() {
        synchronized (TestSynchronized.class) { //可以跨实例加锁
            sum+=5;
        }
    }

    //reentrantLock 非公平模式与synchronized基本一样
    void increaseSum6() {
        reentrantLock.lock();
        sum+=6;
        reentrantLock.unlock();
    }

    private final MyAQS.MyMutex myMutex=new MyAQS.MyMutex();
    void increaseSumWithMyMutex() {
        myMutex.acquire(1);
        sum+=1;
        myMutex.release(1);
    }

    private final MyAQS.MySemaphore mySemaphore=new MyAQS.MySemaphore(1);
    void increaseSumWithMySemaphore() {
        mySemaphore.acquireShared(1);
        //1.必须使用CAS且判断其返回值，CAS很容易失败的！
        //2.推荐加上自旋等待一会
        sum+=1;
        mySemaphore.releaseShared(1);
    }

    public void withSync() throws InterruptedException {
        //倒数等待锁
        final CountDownLatch doneSignal = new CountDownLatch(THREAD_NUM);

        List<Thread> threads=new ArrayList<>();
        for (int i = 0; i < THREAD_NUM; i++) {
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        increaseSum3();
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) { //sleep过程中调用interrupt()会抛出InterruptedException
                            e.printStackTrace();
                        }
                    }//for
                    doneSignal.countDown();
                }
            });
            threads.add(thread);
            thread.start();
        }
//        for (Thread t:threads) {
//            t.join(); //join：阻塞等待线程执行完成
//        }

        //使用 倒数等待锁 作为任务完成的标志，等待子线程执行完
        doneSignal.await();
    }

    public static void main(String[] args) {
        TestSynchronized test=new TestSynchronized();
        try {
//            test.raceCondition();
            test.withSync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(test.sum);
    }
}
